/**
 *  Copyright 24.08.2012 oliver.keusemann@googlemail.com
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package de.xelerity.geolessi.types.mixed;

import java.io.Serializable;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import de.xelerity.geolessi.preferences.Preferences;

public class Sector implements Serializable {

	private static final long serialVersionUID = 792060852356867836L;

	private static Log log = LogFactory.getLog(Sector.class);

	static double SectorSizeX = 0.01 * 1.90;
	static double SectorSizeY = 0.006598 * 1.90;

	int X;
	int Y;

	int maxKantenLaenge;

	Koordinate Center;

	Koordinate[] Sectorgrenzen = null;

	public Sector(Koordinate K) {

		double longitude = K.getLongitude();

		double d = longitude - Preferences.Germany_WEST;

		this.X = (int) (d / SectorSizeX);

		double lattitude = K.getLatitude();

		d = lattitude - Preferences.Germany_SOUTH;

		this.Y = (int) (d / SectorSizeY);

		maxKantenLaenge = (int) Math.max(K.distance(K.getLatitude(), K.getLongitude() + SectorSizeX),
				K.distance(K.getLatitude() + SectorSizeY, K.getLongitude()));

		double xx = Preferences.Germany_WEST + (this.X * SectorSizeX) + (SectorSizeX / 2.0);
		double yy = Preferences.Germany_SOUTH + (this.Y * SectorSizeY) + (SectorSizeY / 2.0);

		this.Center = new Koordinate(Math.round(yy * 1000000) / 1000000.0, Math.round(xx * 1000000) / 1000000.0);

	}

	public Sector(int x, int y) {
		X = x;
		Y = y;

		double xx = Preferences.Germany_WEST + (this.X * SectorSizeX) + (SectorSizeX / 2.0);
		double yy = Preferences.Germany_SOUTH + (this.Y * SectorSizeY) + (SectorSizeY / 2.0);

		this.Center = new Koordinate(Math.round(yy * 1000000) / 1000000.0, Math.round(xx * 1000000) / 1000000.0);

		maxKantenLaenge = (int) Math.max(Center.distance(Center.getLatitude(), Center.getLongitude() + SectorSizeX),
				Center.distance(Center.getLatitude() + SectorSizeY, Center.getLongitude()));

	}

	public int getMaxKatenLaenge() {
		return maxKantenLaenge;
	}

	public Koordinate getCenter() {
		return Center;
	}

	public static Sector fromKoordinate(Koordinate K) {
		if (K == null || !K.isValid()) {
			return null;
		}
		return new Sector(K);
	}

	public String toString() {
		return "[" + this.X + "," + this.Y + "], " + this.maxKantenLaenge + ", " + this.Center;
	}

	public int getX() {
		return X;
	}

	public int getY() {
		return Y;
	}

	public boolean SectorIsCompleteInCircleArea(Koordinate pKreisCenter, long pKreisradius) {

		if (pKreisCenter == null || !pKreisCenter.isValid()) {
			return false;
		}
		if (Sectorgrenzen == null) {
			getSectorGrenzenKoordinaten();
		}

		// Koordinate NW = Sectorgrenzen[0];
		// Koordinate NO = Sectorgrenzen[1];
		// Koordinate SO = Sectorgrenzen[2];
		// Koordinate SW = Sectorgrenzen[3];

		double dis = pKreisCenter.distance(Sectorgrenzen[0]);
		if (dis > pKreisradius)
			return false;

		dis = pKreisCenter.distance(Sectorgrenzen[1]);
		if (dis > pKreisradius)
			return false;

		dis = pKreisCenter.distance(Sectorgrenzen[2]);
		if (dis > pKreisradius)
			return false;

		dis = pKreisCenter.distance(Sectorgrenzen[3]);
		if (dis > pKreisradius)
			return false;

		return true;
	}

	public static SectorClusterDef getSectorsInCircleAreaApprox(Koordinate pKreisCenter, long pKreisradius) {

		if (pKreisCenter == null || !pKreisCenter.isValid()) {
			Log log = LogFactory.getLog(Sector.class);
			log.error("Illegal Arguments/1. <null> oder Koordinate nicht gueltig");
			return null;
		}

		double xleft = pKreisCenter.getLongitude() - Koordinate.MeterinUSG84_X(pKreisCenter, pKreisradius);
		double yleft = pKreisCenter.getLatitude();

		double xrechts = pKreisCenter.getLongitude() + Koordinate.MeterinUSG84_X(pKreisCenter, pKreisradius);
		double yrechts = pKreisCenter.getLatitude();

		double xoben = pKreisCenter.getLongitude();
		double yoben = pKreisCenter.getLatitude() + Koordinate.MeterinUSG84_Y(pKreisCenter, pKreisradius);

		double xunten = pKreisCenter.getLongitude();
		double yunten = pKreisCenter.getLatitude() - Koordinate.MeterinUSG84_Y(pKreisCenter, pKreisradius);

		int[] sectorCoord = Sector.getSectorCoord(new Koordinate(yleft, xleft));
		int xs = sectorCoord[0];

		sectorCoord = Sector.getSectorCoord(new Koordinate(yrechts, xrechts));
		int xe = sectorCoord[0];

		sectorCoord = Sector.getSectorCoord(new Koordinate(yunten, xunten));
		int ys = sectorCoord[1];

		sectorCoord = Sector.getSectorCoord(new Koordinate(yoben, xoben));
		int ye = sectorCoord[1];

		return new SectorClusterDef(xs, xe, ys, ye);
	}

	public static SectorClusterDef getSectorsInMapRegionArea(Koordinate pMapCenter, double pSpanX, double pSpanY) {

		if (pMapCenter == null || !pMapCenter.isValid()) {
			log.error("Illegal Arguments/2. <null> oder Koordinate nicht gueltig");
			return null;
		}

		double NOX = pMapCenter.getLongitude() + pSpanX / 2.0;
		double NOY = pMapCenter.getLatitude() + pSpanY / 2.0;

		double SWX = pMapCenter.getLongitude() - pSpanX / 2.0;
		double SWY = pMapCenter.getLatitude() - pSpanY / 2.0;

		int[] sectorCoord = Sector.getSectorCoord(new Koordinate(SWY, SWX));
		int xs = sectorCoord[0];

		sectorCoord = Sector.getSectorCoord(new Koordinate(NOY, NOX));
		int xe = sectorCoord[0];

		sectorCoord = Sector.getSectorCoord(new Koordinate(SWY, SWX));
		int ys = sectorCoord[1];

		sectorCoord = Sector.getSectorCoord(new Koordinate(NOY, NOX));
		int ye = sectorCoord[1];

		return new SectorClusterDef(xs, xe, ys, ye);
	}

	public static int[] getSectorCoord(Koordinate K) {
		if (K == null || !K.isValid()) {
			return null;
		}
		int ret[] = { (int) ((K.getLongitude() - Preferences.Germany_WEST) / SectorSizeX),
				(int) ((K.getLatitude() - Preferences.Germany_SOUTH) / SectorSizeY) };
		return ret;
	}

	/**
	 * @return
	 *         result[0] = NordWest - Koordinate
	 *         result[1] = NordOst
	 *         result[2] = SuedOst
	 *         result[3] = SuedWest
	 * 
	 */
	public Koordinate[] getSectorGrenzenKoordinaten() {

		if (Sectorgrenzen != null) {
			return Sectorgrenzen;
		}

		double left = this.Center.getLongitude() - SectorSizeX;
		double right = this.Center.getLongitude() + SectorSizeX;
		double oben = this.Center.getLatitude() + SectorSizeY;
		double unten = this.Center.getLatitude() - SectorSizeY;

		Koordinate NW = new Koordinate(oben, left);
		Koordinate NO = new Koordinate(oben, right);
		Koordinate SO = new Koordinate(unten, right);
		Koordinate SW = new Koordinate(unten, left);

		Sectorgrenzen = new Koordinate[] { NW, NO, SO, SW };
		return Sectorgrenzen;
	}

	public int hashCode() {
		return (X + "," + Y).hashCode();
	}

	public boolean equals(Object o) {
		if (o == null)
			return false;
		if (o instanceof Sector)
			return ((Sector) o).hashCode() == this.hashCode();
		else
			return false;
	}

	public static void main(String[] args) {

		Koordinate FLENSBURG = new Koordinate(54.799102, 9.394455);
		Koordinate FRANKFURT = new Koordinate(50.110011, 8.666038);
		Koordinate KOSTANZ = new Koordinate(47.672786, 9.167633);
		Koordinate GOERLITZ = new Koordinate(51.788232, 14.313354);

		System.out.println(FLENSBURG.distance(FLENSBURG.getLatitude(), FLENSBURG.getLongitude() + SectorSizeX));
		System.out.println(FLENSBURG.distance(FLENSBURG.getLatitude() + SectorSizeY, FLENSBURG.getLongitude()));

		System.out.println("-");
		System.out.println(FRANKFURT.distance(FRANKFURT.getLatitude(), FRANKFURT.getLongitude() + SectorSizeX));
		System.out.println(FRANKFURT.distance(FRANKFURT.getLatitude() + SectorSizeY, FRANKFURT.getLongitude()));

		System.out.println("-");
		System.out.println(KOSTANZ.distance(KOSTANZ.getLatitude(), KOSTANZ.getLongitude() + SectorSizeX));
		System.out.println(KOSTANZ.distance(KOSTANZ.getLatitude() + SectorSizeY, KOSTANZ.getLongitude()));

		System.out.println("-");
		System.out.println(GOERLITZ.distance(GOERLITZ.getLatitude(), GOERLITZ.getLongitude() + SectorSizeX));
		System.out.println(GOERLITZ.distance(GOERLITZ.getLatitude() + SectorSizeY, GOERLITZ.getLongitude()));

		System.out.println("--------------------------------------------------------");

		Sector S = Sector.fromKoordinate(FLENSBURG);

		new Sector(261, 909);

		System.out.println("Sector:" + S);
		System.out.println("--------------------------------------------------------");

		Koordinate[] sectorGrenzenKoordinaten = S.getSectorGrenzenKoordinaten();

		String bez[] = { "NW", "NO", "SO", "SW" };
		for (int i = 0; i < sectorGrenzenKoordinaten.length; i++) {
			System.out.println(bez[i] + ":" + sectorGrenzenKoordinaten[i]);
		}

		System.out.println("--------------------------------------------------------");

		System.out.println(SectorSizeX);
		System.out.println(Koordinate.MeterinUSG84_X(KOSTANZ, 974));

		System.out.println("--------------------------------------------------------");
		System.out.println(SectorSizeY);
		System.out.println(Koordinate.MeterinUSG84_Y(FRANKFURT, 955));

		long i = GOERLITZ.distance(GOERLITZ.getLatitude() + 0.09, GOERLITZ.getLongitude());
		System.out.println(i);
		System.out.println(Koordinate.MeterinUSG84_Y(GOERLITZ, i));

		getSectorsInCircleAreaApprox(FRANKFURT, 20);

		// 2147483647

	}
}
